/*
 * $ dally project : www client
 * ---------------------------------------------------------------------------------------
 * description :
 *    W3Client ; synchronized www client class
 *    AsyncW3Client ; asynchronized www client class
 *
 *    - HTTP/HTTPS support
 *       - GET method support
 *       - POST method support
 *       - POST Multiparts/form-data method support
 *    - FTP support
 *       - Get support
 *       - Put support
 *    - ETC
 *       - url parse
 *
 * special thanks.
 *    Yun Myungwook, Peter Newman, ...
 *
 * ----------------------------------------------------------------------------------------
 * coded by Heo Yongseon ( gooshin@zzem.net )
 *
 * This code distributed under BSD License Style.
 *
 * -----------------------------------------------------------------------------------------
 * TO DO :
 *
 * -----------------------------------------------------------------------------------------
 * sample
 *
 *   // synchronized(blocked) www client example
 *   #include <iostream>
 *   #include <net/w3c.h>
 *
 *   using namespace std;
 *   using namespace dally;
 *
 *   int main(int argc, char *argv[]){
 *
 *   	W3Client w3;
 *
 *   	if(w3.Connect("http://google.com")){
 *   		if(w3.Request("/")){
 *   			char buf[1024]="\0";
 *   			while(w3.Response(reinterpret_cast<BYTE* >(buf), 1024))
 *   				cout << buf ;
 *   		}
 *   		w3.Close();
 *   	}
 *   	return 0;
 *   }
 *
 *	 // asynchronized www client example
 *   #include <iostream>
 *   #include <net/w3c.h>
 *   #include <wt.h>
 *   #include <windows.h>
 *
 *   using namespace std;
 *   using namespace dally;
 *
 *   CRITICAL_SECTION __cs;
 *
 *   class AsDown : public AsyncW3Client, public IWORKERTHREAD {
 *   public:
 *   	AsDown(unsigned int idx):AsyncW3Client(), IWORKERTHREAD(idx){}
 *   	virtual ~AsDown(){}
 *   private:
 *   	virtual void OnWork(){
 *   		while(true){
 *   			WaitCompleteRequest();
 *
 *   			BYTE buf[1024]="\0";
 *   			while(Response(buf, 1024)){
 *   					::EnterCriticalSection(&__cs);
 *   					cout << reinterpret_cast<char*>(buf);
 *   					::LeaveCriticalSection(&__cs);
 *   					memset(buf, 0x00, 1024);
 *   			}
 *
 *   			::Sleep(500);
 *   		}
 *   	}
 *   };
 *
 *   void CALLBACK __getstatus(  HINTERNET hInternet,
 *    												  DWORD_PTR dwContext,
 *    												  DWORD dwInternetStatus,
 *    												  LPVOID lpvStatusInformation,
 *    												  DWORD dwStatusInformationLength
 *    												  ){
 *
 *   	AsyncW3Client *pcontext=reinterpret_cast<AsyncW3Client*>(dwContext);
 *
 *   	ULONG nbytes=0;
 *    	::EnterCriticalSection(&__cs);
 *    	switch(dwInternetStatus){
 *   	case INTERNET_STATUS_SENDING_REQUEST:
 *   		cout << "request sending..." << endl;
 *   		break;
 *   	case INTERNET_STATUS_REQUEST_SENT:
 *   		{
 *   			ULONG *pnsent=(ULONG*)lpvStatusInformation;
 *   			cout << "bytes sent: " << *pnsent << endl;
 *   			nbytes+=*pnsent;
 *   			cout << "request sent..." << endl;
 *   		}
 *   		break;
 *   	case INTERNET_STATUS_REQUEST_COMPLETE:
 *   		{
 *   			INTERNET_ASYNC_RESULT *pAsyncRes = (INTERNET_ASYNC_RESULT *)lpvStatusInformation;
 *   			cout << "Function call finished" << endl;
 *   			cout << "dwResult: " << pAsyncRes->dwResult << endl;
 *   			cout << "dwError:  " << pAsyncRes->dwError << endl;
 *   			cout.flush();
 *   			pcontext->SetCompleteRequest();
 *    			cout << "request complete..." << endl;
 *   		}
 *   		break;
 *    	}
 *    	::LeaveCriticalSection(&__cs);
 *
 *    	return;
 *   }
 *
 *   int main(int argc, char *argv[]){
 *
 *   	::InitializeCriticalSection(&__cs);
 *
 *   	AsDown client(3);
 *
 *   	if(client.Connect("http://gooshin.zzem.net", __getstatus)){
 *
 *   		__wtstart(client);
 *
 *   		client.Request("/test.php");
 *   		Sleep(5000);
 *
 *   		client.InitializePostArguments();
 *   		client.AddPostArgument("f[]", "d:\\log1.txt", true);
 *   		client.AddPostArgument("f[]", "d:\\log2.txt", true);
 *   		client.AddPostArgument("f[]", "d:\\log3.txt", true);
 *   		client.Request("/test.php", AsDown::reqPostMultipartsFormdata);
 *   		Sleep(5000);
 *
 *   		client.InitializePostArguments();
 *   		client.AddPostArgument("f", "sss");
 *   		client.Request("/test2.php", AsDown::reqPost);
 *   		Sleep(5000);
 *
 *   		__wtwait(client);
 *   		client.Close();
 *   	}
 *
 *   	::DeleteCriticalSection(&__cs);
 *
 *   	return 0;
 *   }
 *
 */
#ifndef __DALLY_HTTP_CLIENT
#define __DALLY_HTTP_CLIENT


#include <windows.h>
#include <winreg.h>
#include <wininet.h>
#include <list>
#include <cstdio>


#define __W3_DEFAULT_AGENT _T("Mozilla/4.0 (compatible; )")
#define __DEFAULT_BOUNDRY_TAG "--MULTI-PARTS-FORM-DATA-BOUNDARY-"
#define __SIZE_HTTP_HEAD_LINE	2048

void __w3curlparse(LPCTSTR lpstrUrl,
                   TCHAR* pszprotocol, size_t sProtocol,
                   TCHAR* pszUser, size_t sUser,
                   TCHAR* pszPwd, size_t sPwd,
                   TCHAR* pszAddress, size_t sAddr,
                   TCHAR* pszUri, size_t sUri,
                   WORD& wPort);

// synchronized www client
class W3Client
{
public:
	enum w3t { w3ftp, w3http, w3https };
	enum w3m { reqGet, reqPost, reqPostMultipartsFormdata };

private:
	typedef struct __HTTP_ARG
	{
		CStringA strName;
		CStringA strValue;
		bool bFile;

		__HTTP_ARG(LPCSTR lpstrName, LPCSTR lpstrValue, bool b = false);
		__HTTP_ARG(LPCSTR lpstrName, const int iValue);
		__HTTP_ARG(LPCSTR lpstrName, const long lValue);
		__HTTP_ARG(LPCSTR lpstrName, const float fValue);
		__HTTP_ARG(LPCSTR lpstrName, const double dValue);

		LPCTSTR type();

		ULONG length();
		ULONG dump(BYTE* pBuf, ULONG /*s*/);

		ULONG length2();
		ULONG dump2(BYTE* pBuf,
		            ULONG ulSize,
		            LPCSTR lpstrBoundry = __DEFAULT_BOUNDRY_TAG);
	} HTTP_ARG, *PHTTP_ARG;

public:
	typedef struct __HTTP_COOKIE
	{
		CString strName;
		CString strValue;

		__HTTP_COOKIE(const __HTTP_COOKIE& c);
		__HTTP_COOKIE(LPCTSTR lpstrName, const TCHAR* szv);
		__HTTP_COOKIE(LPCTSTR lpstrName, const int iValue);
		__HTTP_COOKIE(LPCTSTR lpstrName, const long lValue);
		__HTTP_COOKIE(LPCTSTR lpstrName, const float fValue);
		__HTTP_COOKIE(LPCTSTR lpstrName, const double dValue);
	} HTTP_COOKIE, *PHTTP_COOKIE;

	W3Client();

	virtual ~W3Client();

public:
	bool Connect(LPCTSTR lpstrAddress,
	             LPCTSTR lpstrUser = NULL,
	             LPCTSTR lpstrPassword = NULL,
	             LPCTSTR lpstrAgent = __W3_DEFAULT_AGENT,
				 LPCTSTR lpProxy = NULL);
	virtual bool Connect(LPCTSTR lpstrAddress, INTERNET_PORT wPort,
	                     LPCTSTR lpstrUser = NULL,
	                     LPCTSTR lpstrPassword = NULL,
	                     w3t t = w3http, LPCTSTR lpstrAgent = __W3_DEFAULT_AGENT,
						 LPCTSTR lpProxy = NULL);
	LPCTSTR GetURI() const;
	void Close();

	void InitializePostArguments();
	void AddPostArgument(LPCSTR lpstrName, const int iValue);
	void AddPostArgument(LPCSTR lpstrName, const long lValue);
	void AddPostArgument(LPCSTR lpstrName, const float fValue);
	void AddPostArgument(LPCSTR lpstrName, const double dValue);
	void AddPostArgument(LPCSTR lpstrName, LPCSTR lpstrValue, bool bfile = false);

	void InitializeCookies();
	void AddCookie(LPCTSTR lpstrName, const double dValue);
	void AddCookie(LPCTSTR lpstrName, const float fValue);
	void AddCookie(LPCTSTR lpstrName, const long lValue);
	void AddCookie(LPCTSTR lpstrName, const int iValue);
	void AddCookie(LPCTSTR lpstrName, LPCTSTR lpstrValue);

	bool Request(LPCTSTR lpstrUri, w3m m = reqGet, LPCTSTR lpstrRef = NULL);
	ULONG Response(BYTE* pBuf, ULONG ulLen);
	unsigned int QueryResult();
	LPCTSTR  QueryContentType();
	ULONG QueryContentLength();
	ULONG QueryCookie(BYTE* pBuf, ULONG ulLen, ULONG ulIdx = 0);
	ULONG QueryRawHeader(BYTE* pBuf, ULONG ulLen);

	bool PutFile(LPCTSTR lpstrUri, LPCTSTR szfile, bool bAscii = false);
	bool GetFile(LPCTSTR lpstrUri, LPCTSTR szfile, bool bAscii = false);
	ULONG PutFile(LPCTSTR lpstrUri, BYTE* pBuf, ULONG ulLen, bool bAscii = false);
	ULONG GetFile(LPCTSTR lpstrUri, BYTE* pBuf, ULONG ulLen, bool bAscii = false);

protected:
	ULONG GetMultiPartsFormDataLength();
	void FreeMultiPartsFormData(BYTE* pBuf);
	ULONG AllocMultiPartsFormData(BYTE*& prBuf, LPCSTR lpstrPoundry = __DEFAULT_BOUNDRY_TAG);
	ULONG GetPostData(BYTE* pBuf, ULONG ulLen);
	ULONG GetPostArgumentsLength();

private:
	virtual bool RequestPost2(LPCTSTR lpstrUri, LPCTSTR lpstrRef = NULL);
	virtual bool RequestPost(LPCTSTR lpstrUri, LPCTSTR lpstrRef = NULL);
	virtual bool RequestGet(LPCTSTR lpstrUri, LPCTSTR lpstrRef = NULL);

protected:
	HINTERNET m_hOpen; // internet open handle
	HINTERNET m_hConnection; // internet connection handle
	HINTERNET m_hRequest; // internet request handle
	CString m_strAddress;
	CString m_strUser;
	CString m_strPassword;
	CString m_strUri;
	WORD m_wPort;
	w3t m_eType;

	std::list<HTTP_ARG*> m_listArgs;
	std::list<HTTP_COOKIE*> m_listCookies;
};

// Asynchronized www client
class AsyncW3Client : public W3Client
{
public:
	AsyncW3Client();

public:
	bool Connect(LPCTSTR lpstrAddress,
	             INTERNET_STATUS_CALLBACK lpfn,
	             LPCTSTR lpstrUser = NULL,
	             LPCTSTR lpstrPassword = NULL,
	             LPCTSTR lpstrAgent = __W3_DEFAULT_AGENT);

	bool Connect(LPCTSTR lpstrAddress, WORD wPort,
	             INTERNET_STATUS_CALLBACK lpfn,
	             LPCTSTR lpstrUser = NULL, LPCTSTR lpstrPassword = NULL,
	             w3t t = w3http, LPCTSTR lpstrAgent = __W3_DEFAULT_AGENT);

	bool Request(LPCTSTR lpstrUri, w3m m = reqGet, LPCTSTR lpstrRef = NULL);
	ULONG Response(BYTE* pBuf, ULONG ulLen);

public:
	void SetCompleteRequest();
	bool WaitCompleteRequest(ULONG ntime = INFINITE);

private:
	virtual bool RequestPost2(LPCTSTR lpstrUri, LPCTSTR lpstrRef = NULL);
	virtual bool RequestPost(LPCTSTR lpstrUri, LPCTSTR lpstrRef = NULL);
	virtual bool RequestGet(LPCTSTR lpstrUri, LPCTSTR lpstrRef = NULL);

private:
	HANDLE m_hCompleteRequestEvent;
};

#endif // !defined(__DALLY_HTTP_CLIENT)
